﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/********************************************************
 * ADO.NET 2.0 Data Provider for SQLite Version 3.X
 * Written by Robert Simpson (robert@blackcastlesoft.com)
 * 
 ********************************************************/

using System;
using System.Data;
using System.Data.Common;
using System.Collections.Generic;
using System.Globalization;
using EffiProzDB;
using EffiProzDB.Lib;
using EffiProzDB.DataTypes;

namespace System.Data.EffiProzDB
{
    internal sealed class EffiProzDBStatement : IDisposable
    {
      
        /// <summary>
        /// The command text of this SQL statement
        /// </summary>
        string _sqlStatement;
       
       
        /// <summary>
        /// Command this statement belongs to (if any)
        /// </summary>
        EffiProzDBCommand _command;

        /**
         * Description of result set metadata. 
         */
        Result _rsmdDescriptor;

        /** Description of parameter metadata. */
        Result _pmdDescriptor;


        Result _resultOut;

        /**
         * The id with which this object's corresponding
         * {@link org.hsqldb.CompiledStatement CompiledStatement}
         * object is registered in the engine's
         * {@link org.hsqldb.CompiledStatementManager CompiledStatementManager}
         * object.
         */
        int _statementID = -1;

        bool _isRowCount;

        /** The Names of the parameters. */
        string[] _parameterNames;

        /** The SQL types of the parameters. */
        int[] _parameterTypes;

        /** The (IN, IN OUT, or OUT) modes of parameters */
        int[] _parameterModes;

        /** Flags for bound variables. */
        bool[] _parameterSet;

        /** The parameter values for the next non-batch execution. */
        Object[] _parameterValues;


        internal EffiProzDBStatement(EffiProzDBCommand command, string strCommand)
        {
            _command = command;
            _sqlStatement = strCommand;           

            PrepareStatement();
        }

        internal Result Execute(EffiProzDBParameterCollection parameters , int maxrows)
        {
            Result resultIn;

            BindParameters(_command.Parameters);

            try
            {
                _resultOut.setMaxRows(maxrows);
                _resultOut.setParameterData(_parameterValues);           
                resultIn = _command.Connection.sessionProxy.execute(_resultOut);
            }
            catch (HsqlException e)
            {
                throw EffiProzDBException.GetException(e);
            }

            if (resultIn.isError())
            {
                EffiProzDBException.throwError(resultIn);
            }

            return resultIn;
        }

        internal void BindParameters(EffiProzDBParameterCollection paramCollection)
        {
            DbParameter para;
            string paraName;
            for (int i = 0; i < _parameterNames.Length; i++ )
            {
                paraName = "@"+_parameterNames[i];
                para = _command.Parameters[paraName];

                if (para.Direction != ParameterDirection.Input)
                    throw new NotSupportedException(para.Direction.ToString());

                if(para == null)
                    throw new ArgumentException("Missing Parameter "+paraName);
                _parameterValues[i] = ConvertTo(para.Value,_parameterTypes[i]);
            }

        }


        internal object ConvertTo(object val, int type)
        {
            if (val == null)
                return val;

            if (val == DBNull.Value)
                return null;

            switch (type)
            {
                case Types.BOOLEAN:
                    return Convert.ToBoolean(val);
                case Types.TINYINT:           
                case Types.SMALLINT:          
                case Types.INTEGER:
                    return Convert.ToInt32(val);
                case Types.BIGINT:
                    return Convert.ToInt64(val);
                case Types.REAL:                  
                case Types.FLOAT:
                case Types.DOUBLE:
                    return Convert.ToDouble(val);
                case Types.DECIMAL:
                case Types.NUMERIC:
                    return Convert.ToDecimal(val);
                case Types.CHAR:
                case Types.VARCHAR:
                case Types.LONGVARCHAR:
                case Types.VARCHAR_IGNORECASE:
                    return Convert.ToString(val);
                case Types.BINARY:
                case Types.LONGVARBINARY:             
                    return new Binary((byte[])val,true);
                case Types.DATE:
                case Types.TIME:
                case Types.TIMESTAMP:
                    return Convert.ToDateTime(val);
                case Types.OTHER:
                    return new DotNetObject(val);
            }
            return null;
        }
    


        internal void PrepareStatement()
        {
            _resultOut = new Result(ResultConstants.SQLPREPARE);           
            _resultOut.setMainString(_sqlStatement);

            Result Resultin = _command.Connection.sessionProxy.execute(_resultOut);

            if (Resultin.isError())
            {
                EffiProzDBException.throwError(Resultin);
            }

            Iterator i;
            i = Resultin.iterator();

            try
            {
                Object[] row;

                // PREPARE_ACK
                row = (Object[])i.next();
                _statementID = ((Result)row[0]).getStatementID();

                // DATA - isParameterDescription == false
                row = (Object[])i.next();
                _rsmdDescriptor = (Result)row[0];
                _isRowCount = _rsmdDescriptor.mode == ResultConstants.UPDATECOUNT;

                // DATA - isParameterDescription == true
                row = (Object[])i.next();
                _pmdDescriptor = (Result)row[0];
                _parameterTypes = _pmdDescriptor.metaData.getParameterTypes();
                _parameterValues = new Object[_parameterTypes.Length];
                _parameterSet = new bool[_parameterTypes.Length];
                _parameterModes = _pmdDescriptor.metaData.paramMode;
                _parameterNames = _pmdDescriptor.metaData.colNames;
                
            }
            catch (Exception e)
            {
                throw Trace.error(Trace.GENERAL_ERROR, e.ToString());
            }

            _resultOut = new Result(ResultConstants.SQLEXECUTE, _parameterTypes,
                                _statementID);
        }

        public void Close()
        {
            if (_statementID != -1)
            {
                try
                {

                    _command.Connection.sessionProxy.execute(
                                       Result.newFreeStmtRequest(_statementID));
                    _statementID = -1;
                    _rsmdDescriptor = null;
                    _pmdDescriptor = null;
                }
                catch (Exception) { }
            }
        }

        public void Dispose()
        {
            Close();
        }
    }
}
